################################################################################
#  File: lab5.py                                                               #
#  Name: Yuan Li                                                               #
#  Andrew ID: yuanli1                                                          #
#  Class: 15-110 Summer 2 2011                                                 #
#  Date: July 24, 2011                                                         #
################################################################################

from Tkinter import *
import random

# Hard-coded global state constants
# Feel free to play around with these
# Window size constant. Use this for creating the window.
windowSize = 800
# Triangle *height* constant. Used by initTriangle
triSize = 800
# The recursion limit.
maxDepth = 6000
# Initial 2D coordinate for the chaos game method.
# Use this to start off your chaos function.
anchor = (0, 0)
# Command to tell Python to increase the recursion limit.
sys.setrecursionlimit(maxDepth + 100)

# Computes an initial equilateral triangle that is centered in the window.
# Note that ax, ay, bx, by, cx, and cy are global variables that can be
# accessed by other functions after initTriangle() is called
def initTriangle(windowSize, triSize):
	global ax, ay
	global bx, by
	global cx, cy

	(ax, ay) = (windowSize / 2, (windowSize - triSize) / 2)
	(bx, by) = (ay, (windowSize + triSize) / 2)
	(cx, cy) = (by, by)
	return

# Function that provides a random vertex.
def randomVertex():
	global ax, ay
	global bx, by
	global cx, cy

	# Chooses a random int from 0 to 2 and returns the corresponding vertex
	vertexList = [(ax, ay), (bx, by), (cx, cy)]
	i = random.randint(0, 2)
	return vertexList[i]

################################################################################
#                            STUDENT CODE GOES HERE                            #
################################################################################

# Calculate the midpoint of two 2D points
def midpoint(pointA, pointB):

	pointAB = ( (pointA[0]+pointB[0])/2, (pointA[1]+pointB[1])/2 )

	return pointAB

# Function that draws one triangle
def drawTriangle(triangle):
	global widgetDict
	
	canvas = widgetDict["canvas"]
	canvas.create_polygon(triangle)
	return

# Recursive function that generates the Sierpinski triangle
def sierpinski(depth, triangle):
	
	# Check parameter (maybe slow down the speed of this function)
	if depth < 0 or len(triangle) != 3 or len(triangle[0]) != 2:
		return -1

	# the coords should be within the canvas
	for point in triangle:
		for coord in point:
			if coord < 0 or coord > 800:
				return -1
			

	# Base case
	if depth == 0:
		drawTriangle(triangle)
		return 0


	# Recursive case
	depth -= 1
	up = triangle[0]
	left = triangle[1]
	right = triangle[2]
	leftMid = midpoint(up,left)
	bottomMid = midpoint(left,right)
	rightMid = midpoint(up,right)
	
	#draw upper triangle
	sierpinski(depth, (up, leftMid, rightMid))
	#draw left triangle
	sierpinski(depth, (leftMid, left, bottomMid))
	#draw right triangle
	sierpinski(depth, (rightMid, bottomMid, right))
	

	# you will not enter here (finally, the program will reach here)
	return 0

	
##############################################




# Function that draws one pixel
def drawPixel(point):
	global widgetDict
	
	canvas = widgetDict["canvas"]
	#  canvas.create_rectangle(ax,ay,bx,by,width=3)
	"""
	ax,ay
		*########
        #		#
		#		#
		#		#
		########*  bx,by

	"""
	# plot a point
	canvas.create_rectangle(point,point)
	return

# Recursive function that generates n points
def chaos(n, start):
	# check input parameter
	if n < 0:
		return -1
	if start[0] < 0 or start[0] > 800:
		return -1
	if start[1] < 0 or start[0] > 800:
		return -1

	# Base case
	if n == 0:
		return 0;
	################################



	# Recursive case
	n -= 1
	# pick a random vertex from a,b,c
	vertex = randomVertex()
	#plot the midpoint
	middle = midpoint(start,vertex)
	drawPixel(middle)
	#recursive
	chaos(n,middle)

	#program will not reach here (finally, the program will reach here)
	return 0
	
	#####################################


# Wrapper function that makes initial recursive call to sierpinski()
def sierpinskiWrapper():
	global widgetDict

	# Check the input parameters
	canvas = widgetDict["canvas"]
	constrEntry = widgetDict["constrEntry"]
	chaosEntry = widgetDict["chaosEntry"]
	content = constrEntry.get()

	# erase both entry field
	constrEntry.delete(0, END)
	chaosEntry.delete(0, END)

	# wipe the canvas
	canvas.delete(ALL)
	
	# for empty input
	if content == "":
		return -1

	# convert and validate the input parameter
	content = content.split(" ") #split string by whitespace
	try:
		value = int(content[0])
	except:
		return -1
	if value < 0 or value > 9:
		return -1
	#####################################################


	#invoke working function
	sierpinski(value, ((ax,ay), (bx,by), (cx,cy)))

	return 0

# Wrapper function that makes initial recrusive call to chaos()
def chaosWrapper():
	global widgetDict

	# Check the input parameters
	canvas = widgetDict["canvas"]
	constrEntry = widgetDict["constrEntry"]
	chaosEntry = widgetDict["chaosEntry"]
	content = chaosEntry.get()

	# erase both entry field
	constrEntry.delete(0, END)
	chaosEntry.delete(0, END)
	
	# wipe the canvas
	canvas.delete(ALL)

	# for empty input
	if content == "":
		return -1

	#convert and validate the input parameter
	content = content.split(" ")
	try:
		value = int(content[0])
	except:
		return -1
	if value < 1 or value > maxDepth:
		return -1
	##########################################

	#invoke working function
	chaos(value, randomVertex())
	return 0
	
	
	

	return

# The main function that sets up the Tk window and canvas widgets
def run():
	global widgetDict

	# Initialize the original triangle
	initTriangle(windowSize, triSize)

	# Setup the canvas
	root = Tk()
	root.title("lab5")
	canvas = Canvas(root, width=800, height=800)
	constrButton = Button(root, text="Constructive", command=sierpinskiWrapper)
	chaosButton = Button(root, text="Chaos", command=chaosWrapper)
	constrEntry = Entry(root, width=35)
	chaosEntry = Entry(root, width=35)
	#canvas.pack()  since you layout the canvas, you do not need pack() here	
	
	# Layout widgets
	canvas.grid(row=0,column=0, columnspan=4)
	constrButton.grid(row=1, column=0)
	constrEntry.grid(row=1, column=1)
	chaosButton.grid(row=1, column=2)
	chaosEntry.grid(row=1, column=3)


	# Save the handle to the dictionary
	widgetDict = {}
	widgetDict["canvas"] = canvas
	widgetDict["constrEntry"] = constrEntry
	widgetDict["chaosEntry"] = chaosEntry
	
	#################################################

	# Loop
	root.mainloop()

	return





# Driver #####################################

run()

